using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using Sublight.Plugins.SubtitleProvider.Types;
using System.Drawing;
using System.Diagnostics;
using System.IO;
using System.Collections;
using Sublight.Plugins.SubtitleProvider;


namespace Sublight.Plugins.SubtitleProvider.BuiltIn
{
    public class SubDivx : BaseSubtitleProvider, ISubtitleProvider
    {
        
        #region ISubtitleProvider Members

        public SubtitleBasicInfo[] Search(string title, SearchFilter[] filter, out string error)
        {
            try
            {
                error = null;

                string url =
                    string.Format("http://www.subdivx.com/index.php?accion=5&masdesc=&buscar={0}&oxfecha=2",
                                        HttpUtility.UrlEncode(title));
                string url2 =
                    string.Format("http://www.subdivx.com/index.php?pg=1&accion=5&buscar={0}&masdesc=1&idusuario=&nick=&oxfecha=2&oxcd=&oxdown=",
                                        HttpUtility.UrlEncode(title));

                List<SubtitleBasicInfo> lstResult = visitURL(url, filter,null);
                Trace.WriteLine(" lstResult " + lstResult.Count);
                lstResult = visitURL(url2, filter, lstResult);
                Trace.WriteLine(" lstResult " + lstResult.Count);
                Trace.Flush();
                return lstResult.ToArray();
            }
            catch (Exception ex)
            {
                error = string.Format("Error searching subtitles: {0}", ex.Message);
                Debug.WriteLine(error);
                Debug.Flush();
                return null;
            }
        }

        private List<SubtitleBasicInfo> visitURL(string url, SearchFilter[] filter, List<SubtitleBasicInfo> lstResult)
        {
            string response = WebClient.DownloadString(url);
            response = response.Replace("\n", "");
            Trace.WriteLine("url:" + url );//+ "\n" + response);                
            if (string.IsNullOrEmpty(response))
            {
                return null;
            }
            lstResult = parseData(response, filter, lstResult);
            return lstResult;
        }

        #region ISubtitleProvider Parse

        private List<SubtitleBasicInfo> parseData(string data, SearchFilter[] filter, List<SubtitleBasicInfo> lstResult)
        {
            if (lstResult == null)
            {
                lstResult = new List<SubtitleBasicInfo>();
            }

            MatchCollection mc = null;
            //@"<a class=""nu"" href=""http://www[.]subdivx[.]com/(?<subbhtml>.*?)<font face=""verdana"" size=""1"" color=""#000000""> <img src=""http://www[.]subdivx[.]com/img/calif"
            mc = Regex.Matches(data, 
                @"<div id=""menu_titulo_buscador""><a class=""titulo_menu_izq"" href=""http://www[.]subdivx[.]com/(?<subbhtml>.*?)<img src=""bajar_sub[.]gif"" border=""0""></a></div></div>"
                );

            Trace.WriteLine(" matches " + mc.Count);

            foreach (Match m in mc)
            {
                string subhtml = m.Groups["subbhtml"].Value;
                Trace.WriteLine(" subbhtml:" + subhtml);

                MatchCollection mcinner = Regex.Matches(subhtml,
                    //@"X6(?<id>.*?)""><font face=""verdana"" size=""2"" color=""#000000""><b>(?<title>.*?)</b></font></a><br><div align=""center""><font face=""verdana"" size=""1"" color=""black"">(?<release>.*?)</div></font><div align=""right""><font face=""verdana"" size=""1"" color=""black""><b>Downloads:</b>(?<download>.*?)</font><font face=""verdana"" size=""1"" color=""black""><b>Cds:</b>(?<discs>.*?)</font><font face=""verdana"" size=""1"" color=""black""><b>Comentarios:</b>(?<comentarios>.*?)</font><font face=""verdana"" size=""1"" color=""black""><b>Formato:</b>(?<format>.*?)</font><font face=""verdana"" size=""1"" color=""black""><b>Subido por:</b> <a class=""nu"" href=""(?<ownerweb>.*?)"">(?<ownername>.*?)</a> <img src=""http://www[.]subdivx[.]com/pais/(?<lang>.*?)[.]gif"" width=""16"" height=""12""> <b>el</b>(?<date>.*?)</font>"
                    @"X6(?<id>.*?)"">(?<title>.*?)</a></div><img src=""img/calif(?<calif>.*?)[.]gif"" class=""detalle_calif""></div><div id=""buscador_detalle""><div id=""buscador_detalle_sub"">(?<release>.*?)</div><div id=""buscador_detalle_sub_datos""><b>Downloads:</b>(?<download>.*?)<b>Cds:</b>(?<discs>.*?)<b>Comentarios:</b>(?<comentarios>.*?)<b>Formato:</b>(?<format>.*?)<b>Subido por:</b> <a class=""link1"" href=""(?<ownerweb>.*?)"">(?<ownername>.*?)</a> <img src=""http://www[.]subdivx[.]com/pais/(?<lang>.*?)[.]gif"" width=""16"" height=""12""> <b>el</b>(?<date>.*?)<a rel=""nofollow"" href=""(?<downloadurl>.*?)"">"
                    );

                Trace.WriteLine(" Inner matches " + mcinner.Count);

                foreach (Match min in mcinner)
                {
                    Trace.WriteLine(" matches " + min.Groups.Count);
                    /*for (int i = 1; i < min.Groups.Count; i++)Group GroupCurrent = min.Groups[i];if (GroupCurrent.Success){Trace.WriteLine("\tMatched:" + GroupCurrent.Value);}}*/
                    SubtitleInfo sbi = createInfo(min.Groups, filter);
                    if (sbi == null)
                        continue;
                    if (DoesMatchFilter(sbi, filter))
                    {
                        lstResult.Add(sbi);

                        if (MaxResultsReached(filter, lstResult.Count))
                        {
                            return lstResult;
                        }
                    }  
                }
            }
            return lstResult;
        }

        private SubtitleInfo createInfo(GroupCollection mGroups, SearchFilter[] filter)
        {
            Trace.WriteLine(" id:" + mGroups["id"] +
                "\n title:" + mGroups["title"] +
                "\n release:" + mGroups["release"] +
                "\n download:" + mGroups["download"] +
                "\n discs:" + mGroups["discs"] +
                "\n comentarios " + mGroups["comentarios"] +
                "\n format " + mGroups["format"] +
                "\n ownername " + mGroups["ownername"] +
                "\n date " + mGroups["date"] +
                "\n lang " + mGroups["lang"] +
                "\n downloadurl " + mGroups["downloadurl"] +
                "\n calif " + mGroups["calif"] + 
                "\n FIN");

            if (mGroups["downloadurl"] == null || !mGroups["downloadurl"].Success)
                return null;
            if (mGroups["title"] == null || !mGroups["title"].Success)
                return null;

            string subtitleId = mGroups["downloadurl"].Value.Trim(); //mGroups["id"].Value.Trim();
            string subtitleTitle = mGroups["title"].Value.Trim();

            if (subtitleTitle != null && subtitleTitle.IndexOf("</b>") >= 0)
            {
                int i = subtitleTitle.IndexOf("</b>");
                subtitleTitle = subtitleTitle.Substring(0, i);
                Trace.WriteLine("subtitleTitle:" + subtitleTitle);
            }
            SubtitleInfo sbi = new SubtitleInfo();
            sbi.Id = subtitleId;
            sbi.Title = subtitleTitle;
            sbi.Language = LanguageIdToLanguage(mGroups["lang"].Value.Trim());//Language.Spanish; //

            string releaseSt = mGroups["release"].Value.Trim();
            releaseSt = releaseSt.Replace("<b>", "");
            releaseSt = releaseSt.Replace("</b>", "");
            sbi.Release = releaseSt;

            sbi.MediaFormat = MediaFormat.CD;            
            if ( releaseSt.IndexOf("720p") >= 0 || 
                        releaseSt.IndexOf("1080") >= 0 )
            {
                sbi.MediaFormat = MediaFormat.BlueRay;
                sbi.FPS = FPS.FPS_23_976;
            }
            //sbi.MovieType = MovieType.Unknown;
            sbi.Publisher = mGroups["ownername"].Value.Trim();
            try
            {
                int year = parseYear(mGroups["title"].Value);
                if (year > 0)
                {
                    sbi.Year = year;
                }
                
                sbi.DownloadSize = Int16.Parse(mGroups["download"].Value) * 1000;
                sbi.PublishDate = DateTime.ParseExact(mGroups["date"].Value.Trim(), "dd/MM/yyyy", null);// hh:mm:ss tt
                sbi.MediaCount = byte.Parse(mGroups["discs"].Value);
            }
            catch (Exception ex)
            {
                string error = string.Format("Error parsing date: {0}", ex.Message);
                Debug.WriteLine(error);
            }

            Int16 season = parseSeason(mGroups["title"].Value, filter);
            if (season >= 0)
            {
                sbi.Season = season;
            }
            Int16 episode = parseEpisode(mGroups["title"].Value, filter);
            if (episode >= 0)
            {
                sbi.Episode = episode;
            }
            
            return sbi;         
        }

        private int parseYear(string title)
        {
            try
            {
                MatchCollection mc = null;

                mc = Regex.Matches(title, @"\((?<year>[0-9]*?)\)");
                Trace.WriteLine(" year matches " + mc.Count);
                foreach (Match m in mc)
                {
                    int year = -1;
                    if (Int32.TryParse(m.Groups["year"].Value.Trim(), out year))
                    {
                        return year;
                    }
                }
            }
            catch (Exception ex)
            {
                string error = string.Format("Error parsing year: {0}", ex.Message);
                Debug.WriteLine(error);
            }
            return -1;
        }

        private Int16 parseEpisode(string title, SearchFilter[] filter)
        {
            bool hasEpisodeFilter = false;
            if (filter != null)
            {
                foreach (SearchFilter sf in filter)
                {
                    if (sf is Sublight.Plugins.SubtitleProvider.Types.SearchFilterEpisode)
                    {
                        hasEpisodeFilter = true;
                        break;
                    }
                }
            }
            if (!hasEpisodeFilter || string.IsNullOrEmpty(title))
            {
                return -1;
            }

            MatchCollection mc = null;

            mc = Regex.Matches(title, @"E(?<episode>[0-9][0-9]?)");
            Trace.WriteLine(" title" + title +
                            "\n episode matches " + mc.Count);
            foreach (Match m in mc)
            {
                //for (int i = 1; i < m.Groups.Count; i++){                    Group GroupCurrent = m.Groups[i];                    if (GroupCurrent.Success)                    {                        Trace.WriteLine("\tMatched:" + GroupCurrent.Value);                    }                    else                    {                        Trace.WriteLine("\t NO SUCCESS");                    }                }
                string episode = m.Groups["episode"].Value.Trim();
                Trace.WriteLine(" episode:" + episode);
                if (string.IsNullOrEmpty(episode))
                {
                    continue;
                }                
                try {
                    return Int16.Parse(episode);
                }
                catch( Exception ex )
                {
                    string error = string.Format("Error parsing episode: {0}", ex.Message);
                    Debug.WriteLine(error);
                    continue;
                }
            }
            return -1;
        }

        private Int16 parseSeason(string title, SearchFilter[] filter)
        {
            bool hasSeasonFilter = false;
            if (filter != null)
            {
                foreach (SearchFilter sf in filter)
                {
                    if (sf is Sublight.Plugins.SubtitleProvider.Types.SearchFilterSeason)
                    {
                        hasSeasonFilter = true;
                        break;
                    }
                }
            }
            if (!hasSeasonFilter || string.IsNullOrEmpty(title) )
            {
                return -1;
            }

            MatchCollection mc = null;

            mc = Regex.Matches(title, @"S(?<season>[0-9][0-9]?)");
            Trace.WriteLine(" season matches " + mc.Count);
            foreach (Match m in mc)
            {
                string season = m.Groups["season"].Value.Trim();
                Trace.WriteLine(" season:" + season);
                if (string.IsNullOrEmpty(season))
                {
                    continue;
                }
                try {
                    return Int16.Parse(season);
                }
                catch( Exception ex )
                {
                    string error = string.Format("Error parsing season: {0}", ex.Message);
                    Debug.WriteLine(error);
                    continue;
                }
            }
            return -1;
        }

        private Language LanguageIdToLanguage(string id) {
            try {
                if( id == "6") {
                    return Language.Spanish;
                } else {
                    return Language.SpanishArgentina;
                }
            }
            catch {
                return Language.Unknown;
            }
        }

        #endregion

        
        #region ISubtitleProvider Download

        SubtitleProviderDownloadType downloadType = SubtitleProviderDownloadType.Direct;//.Indirect;//

        public byte[] DownloadById(string id)
        {
            //string url2Return = "http://www.subdivx.com/X6" + id;
            string downloadFileUrl = "http://www.subdivx.com/" + id;
            //string url3 = getSecondUrl(url2Return);
            //string downloadFileUrl = getThirdUrl(url3);
            Debug.WriteLine("DownloadById: " + downloadFileUrl);
            //return downloadFile(downloadFileUrl,id);
            return DownloadFileConvertZip(downloadFileUrl, id);
        }

        public string GetDownloadUrl(string id)
        {   
            //string url2Return = "http://www.subdivx.com/X6" + id;
            string urlReturn = "http://www.subdivx.com/" + id;//getSecondUrl(url2Return);
            //string urlReturn = getThirdUrl(url3);
            Debug.WriteLine(" GetDownloadUrl: " + id +
                            "\n urlReturn: " + urlReturn);
            return urlReturn;
        }

        private byte[] DownloadFileConvertZip(string downloadfile, string id)
        {
            MemoryStream ms;
            string fileType;
            if (DownloadFile(downloadfile, out ms, out fileType))
            {
                Debug.WriteLine(" downloadfile: " + downloadfile +
                            "\n Length: " + ms.GetBuffer().Length +
                            "\n fileType: " + fileType );
                if (fileType.Equals("rar"))
                {
                    return Compression.Utility.RarToZip(ms.GetBuffer());
                }
                return ms.GetBuffer();
            }
            Debug.WriteLine(" downloadfile: " + downloadfile +
                            "\n fail: ");
            return new byte[0];
        }


        private static bool DownloadFile(string url, out MemoryStream ms, out string fileType)
        {
            ms = null;
            fileType = null;

            try
            {
                //first request: get response url
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
                HttpWebResponse res = (HttpWebResponse)req.GetResponse();

                //second request: get subtitle data
                req = (HttpWebRequest)WebRequest.Create(url);
                req.Referer = res.ResponseUri.ToString();
                req.KeepAlive = true;
                req.Method = "GET";
                res = (HttpWebResponse)req.GetResponse();

                if (res.ContentLength > 0)
                {
                    //we will write response to memory stream
                    Stream s = res.GetResponseStream();

                    byte[] buffer = new byte[res.ContentLength];
                    ms = new MemoryStream(Convert.ToInt32(res.ContentLength));
                    while (true)
                    {
                        int resLen = s.Read(buffer, 0, buffer.Length);
                        if (resLen <= 0) break;
                        ms.Write(buffer, 0, resLen);
                    }

                    s.Dispose();

                    //check response type
                    string contentDisposition = res.Headers["Content-Disposition"];
                    if (contentDisposition != null)
                    {
                        if (contentDisposition.ToLower().Contains(".rar"))
                        {
                            fileType = "rar";
                        }
                        else if (contentDisposition.ToLower().Contains(".zip"))
                        {
                            fileType = "zip";
                        }
                        else
                        {
                            fileType = null;
                        }
                    }

                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch
            {
                return false;
            }
        }

        private string getThirdUrl(string url3)
        {
            try
            {
                string response = WebClient.DownloadString(url3);

                Trace.WriteLine("url3:" + url3);// + "\n" + response);

                if (string.IsNullOrEmpty(response))
                    return null;

                return parseData3(response);
            }
            catch (Exception ex)
            {
                string error = string.Format("Error searching subtitles 3: {0}", ex.Message);
                Debug.WriteLine(error);
                Debug.Flush();
                return null;
            }
        }

        private string parseData3(string response)
        {
            MatchCollection mc = null;

            mc = Regex.Matches(response, @"favor <a href=""(?<downloadfile>.*?)"">haz click");
            Trace.WriteLine(" matches " + mc.Count);

            foreach (Match m in mc)
            {
                string downloadfile = m.Groups["downloadfile"].Value.Trim();
                Trace.WriteLine(" downloadfile:" + downloadfile);
                return downloadfile;
            }
            return null;
        }

        private byte[] downloadFile(string downloadfile, string id)
        {
            try
            {
                string nameFromURL = System.IO.Path.GetFileName(downloadfile);
                //is file compressed

                string fileName = System.IO.Path.GetTempPath() + "\\" +
                    nameFromURL;

                if (nameFromURL.Contains(".php"))
                {
                    fileName = System.IO.Path.GetTempFileName();

                    if (opened.ContainsKey(id))
                    {
                        // esto significa que ya ha pasado por aqui con este id
                        int i = (int)opened[id];
                        if ( i == 2 )
                        {
                            downloadType = SubtitleProviderDownloadType.Indirect;
                        }
                        Trace.WriteLine(" downloadType:" + downloadType +
                            "\n i = " + i);

                        opened.Remove(id);
                        opened.Add(id, i + 1);
                        nameFromURL = "dummy.rar";
                        fileName = fileName + ".rar";
                    }
                    else
                    {
                        opened.Add(id, 1);
                        nameFromURL = "dummy.zip";
                        fileName = fileName + ".zip";
                    }
                }

                // name of the file in zip mode
                string fileNameZip = null;

                // name of the file inside zip format
                string unzFileName = null;

                Trace.WriteLine(" download to:" + fileName + " nameFromURL:" + nameFromURL);
                WebClient.DownloadFile(downloadfile, fileName);
                /// the file is in fileName
                
                if( UnCompress.GetArchiveFormat(nameFromURL) !=
                        Nomad.Archive.SevenZip.KnownSevenZipFormat.Zip )
                {   
                    byte[] buffer = null;
                    if( UnCompress.GetArchiveFormat(nameFromURL) !=
                            Nomad.Archive.SevenZip.KnownSevenZipFormat.UNKNOW )
                    {
                        // when it is compressed
                        Trace.WriteLine(" file is compressed:");
                        MemoryStream memoryStream = new MemoryStream();
                        unzFileName = UnCompress.uncompressFileToStream(fileName, 0, memoryStream);
                        buffer = memoryStream.GetBuffer();                        
                    }
                    else{
                        Trace.WriteLine(" file is not compressed:");
                        FileStream fsTxt = File.OpenRead(fileName);
                        buffer = new byte[fsTxt.Length];
                        fsTxt.Read(buffer, 0, buffer.Length);
                        Trace.WriteLine(" read txt: " + buffer.Length );
                        fsTxt.Close();
                        unzFileName = nameFromURL;
                    }

                    fileNameZip = System.IO.Path.GetTempPath() + "\\" +
                    nameFromURL + ".zip";

                    /// the file is plain text format so compress it in zip.
                    Compress.compress(fileNameZip, unzFileName, buffer);

                    if(buffer != null){
                        Trace.WriteLine(" bufflenTxt:" + buffer.Length);
                    }
                }
                else
                {
                    // if the file is in zip format then get it
                    Trace.WriteLine(" file is zip:");
                    fileNameZip = fileName;
                }
                
                // here there is a zip file 
                FileStream fsZip = File.OpenRead(fileNameZip);
                byte[] data = new byte[fsZip.Length];
                fsZip.Read(data, 0, data.Length);
                Trace.WriteLine(" read zip: " + data.Length);
                fsZip.Close();
                if (data != null)
                {
                    Trace.WriteLine(" bufflenZip:" + data.Length);
                }

                System.IO.File.Delete(fileName);
                if (fileName.CompareTo(fileNameZip) == 0)
                    System.IO.File.Delete(fileNameZip);
                
                return data;
            }
            catch (Exception ex)
            {
                string error = string.Format("Error downloading: {0}", ex.Message);
                Debug.WriteLine(error);
                Debug.Flush();
            }
            try
            {
                /// if fail then indirect
                downloadType = SubtitleProviderDownloadType.Indirect;
                byte [] buffer = WebClient.DownloadData(downloadfile);
                if (buffer != null)
                {
                    Trace.WriteLine(" bufflen 4:" + buffer.Length);
                    return buffer;
                }
            }
            catch (Exception ex)
            {
                string error = string.Format("Error downloading 2: {0}", ex.Message);
                Debug.WriteLine(error);
                Debug.Flush();
            }
            return null;
        }

        public string getSecondUrl(string url2)
        {
            try
            {
                string response = WebClient.DownloadString(url2);

                Trace.WriteLine("url2:" + url2);// + "\n" + response);

                if (string.IsNullOrEmpty(response))
                    return null;

                return parseData2(response);
            }
            catch (Exception ex)
            {
                string error = string.Format("Error searching subtitles 2: {0}", ex.Message);
                Debug.WriteLine(error);
                Debug.Flush();
                return null;
            }
        }

        private string parseData2(string response)
        {
            MatchCollection mc = null;

            mc = Regex.Matches(response, @"<a class=""nublack""  href=""(?<downloadfile>.*?)""><font size=""6"">");
            Trace.WriteLine(" matches " + mc.Count);

            foreach (Match m in mc)
            {
                string downloadfile = m.Groups["downloadfile"].Value.Trim();
                Trace.WriteLine(" downloadfile:" + downloadfile);
                return downloadfile;
            }
            return null;
        }

        public SubtitleProviderDownloadType DownloadType
        {
            get
            {
                Trace.WriteLine(" downloadType:" + downloadType);
                if (downloadType == SubtitleProviderDownloadType.Indirect)
                {
                    downloadType = SubtitleProviderDownloadType.Direct;
                    return SubtitleProviderDownloadType.Indirect;
                }
                return downloadType;//SubtitleProviderDownloadType.Indirect;// 
            }
        }
        #endregion

        public SubtitleAction[] GetActions(string id)
        {
            Trace.WriteLine(" GetActions:" + id );

            var actions = new List<SubtitleAction>();
            string downidv = string.Format("http://www.subdivx.com/{0}", id);
            var action = new SubtitleAction
            {
                Id = "Download",
                Text = downidv,
                Tag = downidv,
                Action = SubtitleAction.ActionType.OpenUrlInBrowser
            };
            actions.Add(action);
            actions.Add(GetVisitHomePage("http://www.subdivx.com/"));

            return actions.ToArray();
        }

        #endregion

        #region ISubtitleProviderInfo Members


        public System.Drawing.Image Logo
        {
            get { return null; }
        }

        public string ShortName
        {
            get { return "www.subdivx.com"; }
        }

        public string Info
        {
            get { return "www.subdivx.com subtitle provider"; }
        }

        public Version Version
        {
            get { return new Version(0, 1, 0); }
        }

        #endregion

        #region WebClient
        protected WebClient WebClient
        {
            get
            {
                if (m_WebClient == null)
                {
                    m_WebClient = new WebClient();

                    var client = new WebClient { Encoding = Encoding.UTF8 };
                    client.Headers.Add("Accept-Language", "sl");
                    client.Headers.Add("Accept", "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-ms-application, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-ms-xbap, */*");
                    client.Headers.Add("User-Agent", UserAgent);
                }

                return m_WebClient;
            }
        }

        private WebClient m_WebClient;
        #endregion

        
        #region OPENED

        private Hashtable opened = new Hashtable();       

        #endregion
    }
}
